/**
 * @Author: kevin.huang 
 * @Date: 2018-07-22 21:18:18 
 * Copyright (c): kevin.huang Released under MIT License
 * 客户端验证组件 采用JSON api验证规则格式
 * @version 1.0
 内置14种验证规则支持：
 		1 必填验证	{rule : 'require', msg:’自定义信息，有默认，可不要’}
		2 数字验证	{rule : ‘number’, msg:’自定义信息，有默认，可不要’}
		3 整数验证	{rule : 'digits', msg:’自定义信息，有默认，可不要’}
		4 手机号验证	{rule : 'phone', msg:’自定义信息，有默认，可不要’}
		5 座机号验证	{rule : 'telphone', msg:’自定义信息，有默认，可不要’}
		6 电子邮件验证 {rule : 'email', msg:’自定义信息，有默认，可不要’}
		7 中文验证	{rule : 'chchar', msg:’自定义信息，有默认，可不要’}
		8 英文字符验证	{rule : 'enchar', msg:’自定义信息，有默认，可不要’}
		9 URL验证	{rule : 'url', msg:’自定义信息，有默认，可不要’}
		10 输入范围验证	{rule :{ range:[10 ,100] } , msg:’自定义信息，有默认，可不要’}
		11 输入最小长度	{rule :{ minlength:10 } , msg:’自定义信息，有默认，可不要’}
		12 输入最大长度	{rule :{ maxlength:10 } , msg:’自定义信息，有默认，可不要’}
		13 正则表达式验证	{rule :{ regex : /正则表达式/ } , msg:’自定义信息，有默认,可不要’}
		14 远程调用验证	{rule :{remote: 远程url地址}, msg:’自定义信息，有默认可不要’}
 * 
 构造参数说明
 args={
		tagId:{rule : 'require', msg:’该字段必须填写！’},
		..... 	
  }
 * 说明 args={......}对应每个表单标签的规则集合，验证组件已经内置了defOpt中的14种验证规则，
 * msg为自定义的提示信息，每个验证规则都设置了默认的提示信息，msg可以不传
 * tagId:指的是标签id，一个标签可以有多个验证规则如 tagId2:[{rule:'email'},{rule:'require'}]
 * rule:可是是已经定以好的验证常量如require、email、number、digits，或者是一个规则对象如{minlength:3}、{range:[10,100]}
 */
(function (global, factory) {
	if (typeof define === 'function' && define.amd) {
		define(['$B', "config"], function (_$B) {
			return factory(global, _$B);
		});
	} else {
		if(!global["$B"]){
            global["$B"] = {};
        }
        factory(global, global["$B"]);
	}
}(typeof window !== "undefined" ? window : this, function (window, $B) {
	var DEFMSG = $B.config.valid.message;
	var REGEX = $B.config.valid.regex;	
	var $body;
    function _getBody() {
        if (!$body) {
            $body = $(window.document.body);
        }
        return $body;
    }
	/**事件绑定***/
	function bindEvent($t, _this) {
		var objEvt = $._data($t[0], "events");
		if (objEvt && objEvt["click"] && objEvt["mouseout"] && objEvt["keyup"]) {
			return;
		}
		$t.on({
			click: function () {
				var $t = $(this);
				$t.siblings('.k_validate_tip_wrap').remove();
				$t.removeClass("k_input_value_err");
				if (_this.onTipFn) {
					_this.onTipFn.call($t, "");
				}
			},
			keyup: function () {
				var $t = $(this);
				var res = v($t, _this);
				if (res) {
					$t.siblings('.k_validate_tip_wrap').remove();
					$t.removeClass("k_input_value_err");
				}
			}
		});	
		$t.on("mouseleave.kvalidate",function (e) {			
			v($(this), _this);
		});
	}
	/**
	 * 远程验证！
	 * ***/
	function remote(r, $this, $tip,_this) {
		clearTimeout($this.data("remote_req_timer"));
		var timer = setTimeout(function(){
			var txt = $this.val();
			var id = $this.attr("id");
			if(!id || id === ""){
				id = $this.attr("name");
			}
			if (txt !== "") {
				var prs = {};
				prs[id] = txt;
				var url = r.rule["remote"];
				var opts = {
					url: url,
					data: prs,
					ok: function (res) {									
						var msg;
						if (typeof res === "string" && res !== "") {					
							msg = res;						
						}else if($.isPlainObject(res) && res.code === 1){					
							msg = res.message;
						}
						if(msg){
							var char_w = $B.getCharWidth(msg) + 10;
							var $wrap = $this.parent();
							$tip.appendTo($wrap).children(".k_validate_tip_content").css("max_width",char_w).html(msg);
							$this.addClass("k_input_value_err");
							var ofs = $tip.offset();
							var avWidth;
							if(_this.$winBody){
								avWidth =  _this.$winBody.offset().left + _this.$winBody.width() - ofs.left;
							}else{
								avWidth = _getBody().width() - ofs.left;
							}
							if (avWidth < char_w) {
								var tipHeight = $tip.outerHeight();
								$tip.css({
									left: 0,
									top: - tipHeight
								});
								setTimeout(function () {
									$tip.remove();
								}, 2000);
							}
						}
					},
					fail: function (res) {}
				};
				$B.request(opts);
			}
		},800);
		$this.data("remote_req_timer" , timer);	
	}

	function a(r, txt, _this) {
		var rs = [];
		if ($.isPlainObject(r.rule)) {
			var p = true;
			var t = typeof r.rule["regex"];
			if (t !== 'undefined') {
				var re = null;
				try {
					if (t === 'string') {
						re = new RegExp(r.rule["regex"], "i");
					} else {
						re = r.rule["regex"];
					}
				} catch (e) {}
				if (re !== null && txt !== "" && !re.test(txt)) {
					rs.push(typeof r.msg === 'undefined' ? DEFMSG['regex'] : r.msg);
				}
			}
			if (typeof r.rule["minlength"] !== 'undefined') {
				if (txt !== "") {
					//if (txt.replace(/[^\x00-\xff]/gi, "--").length < r.rule["minlength"]) {
					if (txt.length < r.rule["minlength"]) {
						rs.push(typeof r.msg === 'undefined' ? DEFMSG['minlength'].replace('{1}', r.rule["minlength"]) : r.msg);
					}
				}
			}
			if (typeof r.rule["maxlength"] !== 'undefined') {
				if (txt !== "") {
					if (txt.length > r.rule["maxlength"]) {
					//if (txt.replace(/[^\x00-\xff]/gi, "--").length > r.rule["maxlength"]) {
						rs.push(typeof r.msg === 'undefined' ? DEFMSG['maxlength'].replace('{1}', r.rule["maxlength"]) : r.msg);
					}
				}
			}
			if (typeof r.rule["range"] !== 'undefined') {
				var tmp = r.rule["range"];
				var min = tmp[0],
					max = tmp[1];
				if (txt !== "") {
					if (REGEX.number.test(txt)) {
						var i = parseInt(txt);
						if (i < min || i > max) {
							rs.push(typeof r.msg === 'undefined' ? DEFMSG['range'].replace('{1}', min).replace('{2}', max) : r.msg);
						}
					} else {
						rs.push("只能输入数字文本！");
					}
				}
			}
		} else {
			var ok = true;
			switch (r.rule) {				
				case "require":
					if ($.trim(txt) === "") {
						ok = false;
					}
					break;			
				default:
					if (txt !== "" && REGEX[r.rule]) {
						if (!REGEX[r.rule].test(txt)) {
							ok = false;
						}
					}
					break;
			}
			if (!ok) {
				rs.push(typeof r.msg === 'undefined' ? DEFMSG[r.rule] : r.msg);
			}
		}
		return rs;
	}
	function v($this, _this) {
		//console.log("valid >>>>>>>>>");
		var ok = true;
		var $wrap = $this.parent().css("position", "relative");
		var this_pos = $this.position();
		var w = $this.outerWidth() + 1;
		var h = $this.outerHeight() - 2;
		var pos = {
			top: this_pos.top + 3 + "px",
			left: (this_pos.left + w) + "px"
		};		
		
		var id = $this.attr("id");
		if(!id || id === ""){
			id = $this.attr("name");
		}
		var clsId = "validate_" + id;
		$wrap.children("." + clsId).remove();
		var $tip = $("<div style='padding:1px 3px;' class='k_validate_tip_wrap " + clsId + "'><div class='k_validate_tip_top'></div><div style='white-space:nowrap;padding:0' class='k_validate_tip_content'></div></div>").css(pos);
		var rule = _this.rules[id];
		if ($.isPlainObject(rule) || $.isArray(rule)) {
			var txt;
			if ($this.tagName === 'textarea') {
				txt = $.trim($this.val());
			} else {
				txt = $.trim($this.val());
			}
			/**
			 * 下拉复选框情况
			 * ***/
			if ($this.hasClass("k_combox_input")) {
				txt = $this.data("id");
			}
			if ($.isArray(rule)) { //如果一个标签是多个验证规则    
				var remoteRule = null;
				var old_id, oldCompare, go;
				$.each(rule, function () {				
					if ($.isPlainObject(this["rule"]) && typeof this["rule"]["remote"] !== 'undefined') {
						remoteRule = this;
					} else {
						var res = a(this, txt, _this);
						if (res.length > 0) {							
							ok = false;
							var strtip = res.join("");
							if (_this.onTipFn && strtip !== "") {
								_this.onTipFn.call($this, strtip);
							} else {
								var char_w = $B.getCharWidth(strtip) + 10;								
								if ($this.hasClass("k_combox_input")) {
									$wrap = $wrap.parent().css("position","relative");
								}
								var tmpTip = $wrap.children(".k_validate_tip_wrap");
								if(tmpTip.length > 0){
									$tip = tmpTip.children(".k_validate_tip_content").css("max-width",char_w).html(strtip);
									$tip = tmpTip;
								}else{
									$tip.appendTo($wrap).children(".k_validate_tip_content").css("max-width",char_w).html(strtip);
								}
								var ofs = $tip.offset();
								var avWidth;
								if(_this.$winBody){
									avWidth = _this.$winBody.offset().left + _this.$winBody.width() - ofs.left;
								}else{
									avWidth = _getBody().width() - ofs.left;
								}
								if (avWidth < char_w) {
								    var tipHeight = $tip.outerHeight();
									$tip.css({
										left: 0,
										top: - tipHeight
									});
									setTimeout(function () {
										$tip.remove();
									}, 2000);
								}
							}
						} else {
							if (_this.onTipFn) {
								_this.onTipFn.call($this, "");
							}
						}
					}
				});
				if (ok && remoteRule !== null) {
					old_id = "#old_" + id;
					oldCompare = _this.form.find(old_id);
					go = true;
					if (oldCompare.length > 0) {
						if (oldCompare.val() === txt) {
							go = false;
						}
					}
					if (go) {
						remote(remoteRule, $this, $tip,_this);
					}
				}
			} else {
				if (typeof rule["remote"] !== 'undefined') {
					old_id = "#old_" + id;
					oldCompare = _this.form.find(old_id);
					go = true;
					if (oldCompare.length > 0) {
						if (oldCompare.val() === txt) {
							go = false;
						}
					}
					if (go) {
						remote(rule, $this, $tip,_this);
					}
				} else {
					var res = a(rule, txt, _this);
					if (res.length > 0) {
						ok = false;
						var strtip = res.join("");
						if (_this.onTipFn && strtip !== "") {
							_this.onTipFn.call($this, strtip);
						} else {
							var char_w = $B.getCharWidth(strtip) + 10;
							$tip.appendTo($wrap).children(".k_validate_tip_content").css("max-width",char_w).html(strtip);
							var ofs = $tip.offset();
							var avWidth;
							if(_this.$winBody){
								avWidth =  _this.$winBody.offset().left + _this.$winBody.width() - ofs.left;
							}else{
								avWidth = _getBody().width() - ofs.left;
							}
							if (avWidth < char_w) {
								var tipHeight = $tip.outerHeight();
								$tip.css({
									left: 0,
									top: - tipHeight
								});
								setTimeout(function () {
									$tip.remove();
								}, 2000);
							}
						}
					} else {
						if (_this.onTipFn) {
							_this.onTipFn.call($this, "");
						}
					}
				}
			}
		}
		$this.attr("valid", ok);
		if(ok){//验证通过
			var $p = $this.parent();
			if($this.hasClass("k_combox_input")){
				$p = $p.parent();
			}
			$p.children(".k_validate_tip_wrap").remove();
			$this.removeClass("k_input_value_err");
		}else{
			$this.addClass("k_input_value_err");
		}
		return ok;
	}
	/******
	 * 构造函数
	 * @param args:验证规则集合
	 * @param form:包含被验证元素的容器(jquery对象)，可有，可无。无的时候，手工调用valid验证时传入即可
	 * *******/
	$B.Validate = function (args, form ,onTipFn) {
		/******检测当前form是否是在html类型的window中*******/
		var parent = form.parent();
		var windowContent ;
		while(parent.length > 0){
			if(parent.hasClass("k_panel_content")){
				windowContent = parent;
				break;
			}
			parent = parent.parent();
			if(parent[0].tagName === "BODY"){
				break;
			}
		}
		if(windowContent){
			this.$winBody = windowContent;
		}
		this.rules = args;
		if (form) {
			this.form = form;
			this.findFormTag(this.form);
		}
		if(onTipFn){
			this.onTipFn = onTipFn;
		}
	};
	$B.Validate.prototype.setTipFn = function (fn) {
		this.onTipFn = fn;
	};
	$B.Validate.prototype.deleteRule = function (filedName) {
		if (this.rules) {
			delete this.rules[filedName];
		}
	};
	$B.Validate.prototype.addRule = function (filedName, newRules) {
		if (!this.rules) {
			this.rules = {};
		}
		this.rules[filedName] = newRules;
	};
	$B.Validate.prototype.valid = function (form) {
		if (!this.form && typeof form === 'undefined') {
			alert("验证组件必须传入表单对象！");
			return false;
		}
		if (!this.form) {
			this.form = form;
		}
		var _this = this;
		var allTag = this.findFormTag(this.form);
		var isPast = true;
		$.each(allTag, function () {
			var tmp = v(this, _this);
			if (!tmp) {
				isPast = false;
			}
		});
		return isPast;
	};
	$B.Validate.prototype.findFormTag = function (f) {
		var _this = this;
		var allTag = [];
		var allText = f.find("input[type=text]");
		allText.each(function () {
			var $t = $(this);
			bindEvent($t, _this);
			allTag.push($t);
		});
		var allPwdText = f.find("input[type=password]");
		allPwdText.each(function () {
			var $t = $(this);
			bindEvent($t, _this);
			allTag.push($t);
		});
		var allFileText = f.find("input[type=file]");
		allFileText.each(function () {
			var $t = $(this);
			bindEvent($t, _this);
			allTag.push($t);
		});
		var allAreatext = f.find("textarea");
		allAreatext.each(function () {
			var $t = $(this);
			bindEvent($t, _this);
			allTag.push($t);
		});
		var allSelect = f.find("select");
		allSelect.each(function () {
			var $t = $(this);
			bindEvent($t, _this);
			allTag.push($t);
		});
		return allTag;
	};
	return $B.Validate;
}));